﻿using System;
using System.Collections.Generic;
using System.IdentityModel.Selectors;
using System.IO;
using System.Net;
using System.ServiceModel;
using PS.WSSDeploy.Client.Tools;
using PS.WSSDeploy.Contract;
using System.Security.Cryptography.X509Certificates;
using System.ServiceModel.Security;


namespace PS.WSSDeploy.Client
{
    public class WSSDeployClient
    {
        #region public class Params

        public class Params
        {
            private string _serverName;
            public string ServerName
            {
                get
                {
                    if(_serverName == "localhost")
                    {
                        IPHostEntry ips = Dns.GetHostEntry(Dns.GetHostName());
                        return ips.AddressList[0].ToString();
                    }
                    return _serverName;
                }
                
                set
                {
                    _serverName = value;
                }
            }
            public string ServicePort { get; set; }
            public string CertificateFileName { get; set; }
            public string CertificateFilePassword { get; set; }
            public string IdentityDnsName { private get; set; }
            public string DnsName 
            {
                get
                {
                    return string.IsNullOrEmpty(IdentityDnsName) ? ServerName : IdentityDnsName;
                }
            }
        }

        #endregion
        
        //Members 

        private readonly Params prms;

        //Properties

        private X509Certificate2 Certificate { get; set; }


        private EndpointAddress EndpointAddress
        {
            get
            {
                string endPointAddr = "net.tcp://" + prms.ServerName + ":" + prms.ServicePort + "/MyService";
                return (Certificate != null)
                           ?
                               new EndpointAddress(
                                   new Uri(endPointAddr),
                                   EndpointIdentity.CreateDnsIdentity(prms.DnsName))
                    : new EndpointAddress(endPointAddr);
            }
            set { throw new NotImplementedException(); }
        }

        private NetTcpBinding NetTcpBinding
        {
            get
            {
                var binding = new NetTcpBinding { TransactionFlow = false };

                if (Certificate != null)
                {
                    binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
                    binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
                    binding.Security.Mode = SecurityMode.Transport;
                }
                else
                {
                    binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
                    binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
                    binding.Security.Mode = SecurityMode.None;
                }

                binding.MaxReceivedMessageSize = Config.MaxReceivedMessageSize;
                binding.MaxBufferSize = Config.MaxBufferSize;
                binding.MaxBufferPoolSize = Config.MaxBufferPoolSize;
                binding.SendTimeout = new TimeSpan(0, 1, 0);
                binding.ReceiveTimeout = new TimeSpan(0, 1, 0);
                binding.OpenTimeout = new TimeSpan(0, 1, 0);
                binding.CloseTimeout = new TimeSpan(0, 1, 0);

                binding.ReaderQuotas.MaxArrayLength = Config.ReaderQuotas_MaxArrayLength;
                return binding;
            }
            set { throw new NotImplementedException(); }
        }

        //Construction

        public WSSDeployClient(Params _prms)
        {
            prms = _prms;

            try
            {
                if (!string.IsNullOrEmpty(prms.CertificateFileName))
                {
                    Certificate = string.IsNullOrEmpty(prms.CertificateFilePassword) ?
                        new X509Certificate2(prms.CertificateFileName)
                        :
                        new X509Certificate2(prms.CertificateFileName, prms.CertificateFilePassword);
                }
            }
            catch(Exception ex)
            {
                throw new Exception(string.Format("Certificate: {0}", ex.Message));
            }
        }

        //Operations

        public void ProxyOperation(Action<IWSSDeployContract> action)
        {
            var factory = new ChannelFactory<IWSSDeployContract>(NetTcpBinding, EndpointAddress);

            if (Certificate != null)
            {
                factory.Credentials.ClientCertificate.Certificate = Certificate;
                                factory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.Custom;
                factory.Credentials.ServiceCertificate.Authentication.CustomCertificateValidator =
                    new CustomX509CertificateValidator();
            }

            var channel = factory.CreateChannel();
            
            try
            {
                action(channel);
            }
            finally
            {
                var communicationObject = (ICommunicationObject)channel;
                if (communicationObject.State != CommunicationState.Faulted)
                {
                    communicationObject.Close();
                }
            }
        }

        public void Create(string sandboxSiteUrl, string fileName)
        {
            ProxyOperation(
                proxy =>
                {
                    var wspFile = new WSSFile
                                      {
                                          Name = Path.GetFileName(fileName),
                                          Content = File.ReadAllBytes(fileName)
                                      };
                    proxy.Create(sandboxSiteUrl, wspFile);
                });
        }

        public void Deploy(string fileName, DeployParams deployParams)
        {
            ProxyOperation(
                proxy => proxy.Deploy(Path.GetFileName(fileName), deployParams));
        }

        public void Upgrade(string sandboxSiteUrl, string fileName)
        {
            ProxyOperation(
                proxy =>
                {
                    var wspFile = new WSSFile
                                      {
                                          Name = Path.GetFileName(fileName),
                                          Content = File.ReadAllBytes(fileName)
                                      };
                    proxy.Upgrade(sandboxSiteUrl, wspFile);
                });
        }

        public void Uninstall(string fileName, DeployParams deployParams)
        {
            ProxyOperation(
                proxy => proxy.Uninstall(Path.GetFileName(fileName), deployParams));
        }

        public void CopyToRoot(string sharePointRootFolder, string path)
        {
            ProxyOperation(
                proxy =>
                {
                    WSSFiles files = FileHelper.File2WSSFiles(sharePointRootFolder, path, true);
                    proxy.CopyToRoot(files);
                });
        }

        public void CopyToRoot(List<RootFile> rootFiles)
        {
            ProxyOperation(
                proxy =>
                {
                    var files = FileHelper.Create(rootFiles);
                    proxy.CopyToRoot(files);
                });
        }

        public int CopyToGAC(string path)
        {
            int itemCount = 0;
            ProxyOperation(
                proxy =>
                {
                    WSSFiles files = FileHelper.File2WSSFiles(string.Empty, path, false);
                    if (files.Files.Count == 0)
                    {
                        throw new Exception(string.Format("Folder '{0}' is empty.", path));
                    }
                    proxy.CopyToGAC(files);
                    itemCount = files.Files.Count;
                });
            return itemCount;
        }

        public int CopyTargetToGAC(string targetPath)
        {
            int itemCount = 0;
            ProxyOperation(
                proxy =>
                {
                    WSSFiles files = FileHelper.Target2WSSFiles(targetPath, true);
                    if (files.Files.Count == 0)
                    {
                        throw new Exception(string.Format("Folder '{0}' is empty.", targetPath));
                    }
                    proxy.CopyToGAC(files);
                    itemCount = files.Files.Count;
                });
            return itemCount;
        }


        public int CopyPdbTo(string projectTargetFolder, string serverPdbFolder)
        {
            if (projectTargetFolder == null) throw new ArgumentNullException("projectTargetFolder");
            if (serverPdbFolder == null) throw new ArgumentNullException("serverPdbFolder");

            int itemCount = 0;
            ProxyOperation(
                proxy =>
                {
                    WSSFiles files = FileHelper.TargetPdb2WSSFiles(projectTargetFolder);
                    if (files.Files.Count > 0)
                    {
                        proxy.CopyTo(serverPdbFolder, files);
                        itemCount = files.Files.Count;              
                    }
                });
            return itemCount;
        }

        public void RecycleAppPools()
        {
            ProxyOperation(
                proxy => proxy.RecycleAppPools());
        }

        public void StartRemoteDebuggerMonitor(string loginName, string password)
        {
            ProxyOperation(
                proxy => proxy.StartRemoteDebuggerMonitor(loginName, password));
        }

        public void KillRemoteDebuggerMonitor()
        {
            ProxyOperation(
                proxy => proxy.KillRemoteDebuggerMonitor());
        }

        public void CreateDebugAccount(string loginName, string password)
        {
            ProxyOperation(
                proxy => proxy.CreateDebugAccount(loginName, password));
        }

        public string GetFarmServerList()
        {
            var ret = "";
            ProxyOperation(
                proxy =>
                    {
                        ret = proxy.GetFarmServerList();
                    });
            return ret;
        }

        public string GetWebApplicationList()
        {
            var ret = "";
            ProxyOperation(
                proxy =>
                {
                    ret = proxy.GetWebApplicationList();
                });
            return ret;
        }

        public bool IsDeployed(string sandboxSiteUrl, string fileName)
        {
            var ret = false;
            ProxyOperation(
                proxy =>
                    {
                        ret = proxy.IsDeployed(sandboxSiteUrl, Path.GetFileName(fileName));
                    });
            return ret;
        }

        public bool IsExists(string sandboxSiteUrl, string fileName)
        {
            var ret = false;
            ProxyOperation(
                proxy =>
                {
                    ret = proxy.IsExists(sandboxSiteUrl, Path.GetFileName(fileName));
                });
            return ret;
        }

        public class CustomX509CertificateValidator : X509CertificateValidator
        {
            public CustomX509CertificateValidator()
            {
            }

            public override void Validate(X509Certificate2 certificate)
            {
                //if (certificate == null)
                //{
                //    throw new ArgumentNullException("certificate");
                //}
            }
        }
    }
}
